package org.poem.sql;

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLDataType;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem;
import com.alibaba.druid.sql.ast.statement.SQLTableElement;
import com.alibaba.druid.sql.dialect.mysql.ast.MySqlKey;
import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor;
import com.alibaba.druid.stat.TableStat;
import com.alibaba.druid.stat.TableStat.Name;
import com.alibaba.druid.util.JdbcConstants;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.poem.CanalClientEntryVO;
import org.poem.CanalClientHeadEntityVO;
import org.poem.DateUtils;
import org.poem.create.CanalClientCreateTableColumnEntry;
import org.poem.create.CanalClientCreateTableEntry;
import org.poem.create.CanalClientCreateTableIndexKey;
import org.poem.enums.EnumDataType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author poem
 */
public class SqlUtils {

    private static final Logger logger = LoggerFactory.getLogger(SqlUtils.class);


    public static final Integer MAX_SIZE = 100;


    /**
     * 生成插入的sql
     *
     * @param table     需要插入的表
     * @param metadata  查询的数据
     * @param columns   列
     * @param typeZip   数据类型
     * @param currentNo 当前的时间戳
     * @return
     */
    public static List<String> createMysqlInsertSql(String table,
                                                    List<Map<String, Object>> metadata,
                                                    List<String> columns,
                                                    Map<String, String> typeZip,
                                                    String currentNo) {
        return createInsertSql(table, metadata, columns, typeZip, currentNo, EnumDataType.MYSQL);
    }

    /**
     * 生成插入的sql
     *
     * @param table        需要插入的表
     * @param metadata     查询的数据
     * @param columns      列
     * @param typeZip      数据类型
     * @param enumDataType 数据库的类型
     * @return
     */
    public static List<String> createInsertSql(String table,
                                               List<Map<String, Object>> metadata,
                                               List<String> columns,
                                               Map<String, String> typeZip,
                                               EnumDataType enumDataType) {
        return createInsertSql(table, metadata, columns, typeZip, DateUtils.formatDateTime(new Date()), enumDataType);
    }

    /**
     * 生成插入的sql
     *
     * @param table        需要插入的表
     * @param metadata     查询的数据
     * @param columns      列
     * @param typeZip      数据类型
     * @param currentNo    当前的时间戳
     * @param enumDataType 数据库的类型
     * @return
     */

    static List<String> createInsertSql(String table,
                                        List<Map<String, Object>> metadata,
                                        List<String> columns,
                                        Map<String, String> typeZip,
                                        String currentNo,
                                        EnumDataType enumDataType) {
        if (EnumDataType.MYSQL.getType().equalsIgnoreCase(enumDataType.getType())) {
            return SqlHelperHandler.createMysqlInsertSql(table, metadata, columns, typeZip, currentNo);
        } else if (EnumDataType.POSTGRES.getType().equalsIgnoreCase(enumDataType.getType())) {
            return SqlHelperHandler.createPostInsertSql(table, metadata, columns, typeZip, currentNo);
        }
        return SqlHelperHandler.createMysqlInsertSql(table, metadata, columns, typeZip, currentNo);
    }

    /**
     * 压缩数据
     *
     * @param columns
     * @param columnsType
     * @return
     */
    public static Map<String, String> zipColumnTypes(List<String> columns, List<String> columnsType) {
        Map<String, String> map = Maps.newHashMap();
        if (columns.size() != columnsType.size()) {
            return map;
        }
        for (int i = 0; i < columns.size(); i++) {
            map.put(columns.get(i), columnsType.get(i));
        }
        return map;
    }


    /**
     * 创建表
     *
     * @param tableName    表面
     * @param colsDataType 数据库的列的类型映射关系
     * @return
     */
    public static String getCreateTableSql(String tableName, Map<String, String> colsDataType) {
        return SqlHelperHandler.getCreateMysqlTableSql(tableName, colsDataType, null);
    }


    /**
     * 创建数据表的sql
     *
     * @param tableName    表
     * @param colsDataType 列名字和列的类型的映射关系
     * @param enumDataType 创建表的数据库类型
     * @return
     */
    public static String getCreateTableSql(String tableName, Map<String, String> colsDataType, EnumDataType enumDataType) {
        return getCreateTableSql(tableName, colsDataType, null, enumDataType);
    }

    /**
     * 创建插入表格的sql
     *
     * @param tableName    表名字
     * @param colsDataType 列名字和列的类型的映射关系
     * @param colsIndexKey 列名字与列的索引的映射管理
     * @param enumDataType 创建表的数据库类型
     * @return 创建的sql
     */
    public static String getCreateTableSql(String tableName, Map<String, String> colsDataType, Map<String, String> colsIndexKey, EnumDataType enumDataType) {
        if (EnumDataType.MYSQL.getType().equalsIgnoreCase(enumDataType.getType())) {
            return SqlHelperHandler.getCreateMysqlTableSql(tableName, colsDataType, colsIndexKey);
        } else if (EnumDataType.POSTGRES.getType().equalsIgnoreCase(enumDataType.getType())) {
            return SqlHelperHandler.getPostgresTableSql(tableName, colsDataType, colsIndexKey);
        }
        return SqlHelperHandler.getCreateMysqlTableSql(tableName, colsDataType, colsIndexKey);
    }

    /**
     * 根据条件翻译成sql
     * sql default  mysql
     *
     * @param entry                 key
     * @param canalClientHeadEntityVO 对象
     * @param enumDataType          数据库类型
     * @return sql
     */
    public static String createUpdateSqlByKeyColumn(CanalClientEntryVO entry, CanalClientHeadEntityVO canalClientHeadEntityVO, EnumDataType enumDataType) {
        if (EnumDataType.MYSQL.getType().equalsIgnoreCase(enumDataType.getType())) {
            return SqlHelperHandler.getUpdateMysqlTableByKey(entry, canalClientHeadEntityVO);
        } else if (EnumDataType.POSTGRES.getType().equalsIgnoreCase(enumDataType.getType())) {
            return SqlHelperHandler.getUpdatePostgresTableByKey(entry, canalClientHeadEntityVO);
        }
        return SqlHelperHandler.getUpdateMysqlTableByKey(entry, canalClientHeadEntityVO);
    }

    /**
     * 根据条件翻译成sql
     * sql default  mysql
     *
     * @param canalClientHeadEntityVO 对象
     * @param enumDataType          数据库类型
     * @return sql
     */
    public static String createUpdateSqlColumn(CanalClientHeadEntityVO canalClientHeadEntityVO, EnumDataType enumDataType) {
        return createUpdateSqlByKeyColumn(null, canalClientHeadEntityVO, enumDataType);
    }

    /**
     * 创建删除的语句
     * sql default  mysql
     *
     * @param entry                 key
     * @param canalClientHeadEntityVO 对象
     * @param enumDataType          数据库类型
     * @return sql
     */
    public static String createDeleteSqlByKeyColumn(CanalClientEntryVO entry, CanalClientHeadEntityVO canalClientHeadEntityVO, EnumDataType enumDataType) {
        if (EnumDataType.MYSQL.getType().equalsIgnoreCase(enumDataType.getType())) {
            return SqlHelperHandler.getDeleteMysqlTableByKey(entry, canalClientHeadEntityVO);
        } else if (EnumDataType.POSTGRES.getType().equalsIgnoreCase(enumDataType.getType())) {
            return SqlHelperHandler.getDeletePostgresTableByKey(entry, canalClientHeadEntityVO);
        }
        return SqlHelperHandler.getDeleteMysqlTableByKey(entry, canalClientHeadEntityVO);
    }

    /**
     * 删除的语句
     * sql default  mysql
     *
     * @param canalClientHeadEntityVO 对象
     * @param enumDataType          数据库类型
     * @return sql
     */
    public static String createDeleteSqlColumn(CanalClientHeadEntityVO canalClientHeadEntityVO, EnumDataType enumDataType) {
        return createDeleteSqlByKeyColumn(null, canalClientHeadEntityVO, enumDataType);
    }


    /**
     * 解析创建表的sql
     *
     * @param sql
     */
    public static List<CanalClientCreateTableEntry> parseMysqlSql(String sql) {
        String dbType = JdbcConstants.MYSQL;
        //格式化输出
        String result = SQLUtils.format(sql, dbType);
        // 缺省大写格式
        List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
        List<CanalClientCreateTableEntry> createTableEntries = Lists.newArrayList();
        //解析出的独立语句的个数
        for (SQLStatement stmt : stmtList) {
            MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();
            stmt.accept(visitor);
            //获取表名称
            Map<Name, TableStat> tabmap = visitor.getTables();
            String tableName = "";
            for (Name name : tabmap.keySet()) {
                tableName = name.toString();
                break;
            }
            CanalClientCreateTableEntry entry = new CanalClientCreateTableEntry();
            entry.setTableName(tableName);
            entry.setDbType(visitor.getDbType());
            List<CanalClientCreateTableColumnEntry> columnEntries = Lists.newArrayList();
            List<CanalClientCreateTableIndexKey> indexKeyList = Lists.newArrayList();
            if (stmt instanceof SQLCreateTableStatement) {
                List<SQLTableElement> sqlTableElements = ((SQLCreateTableStatement) stmt).getTableElementList();
                CanalClientCreateTableColumnEntry columnEntry;
                for (SQLTableElement sqlTableElement : sqlTableElements) {
                    columnEntry = new CanalClientCreateTableColumnEntry();
                    if (sqlTableElement instanceof SQLColumnDefinition) {
                        SQLColumnDefinition sqlColumn = (SQLColumnDefinition) sqlTableElement;
                        SQLDataType dataType = sqlColumn.getDataType();
                        List<SQLExpr> arguments = dataType.getArguments();
                        columnEntry.setDataTypeName(dataType.getName());
                        columnEntry.setColumnName(sqlColumn.getName().getSimpleName());
                        List<Number> dataTypeArguments = Lists.newArrayList();
                        if (!CollectionUtils.isEmpty(arguments)) {
                            for (SQLExpr argument : arguments) {
                                SQLIntegerExpr expr = (SQLIntegerExpr) argument;
                                dataTypeArguments.add(expr.getNumber());
                            }
                        }
                        columnEntry.setDataTypeArguments(dataTypeArguments);
                        columnEntry.setComment(null == sqlColumn.getComment() ? "" : sqlColumn.getComment().toString());
                        columnEntries.add(columnEntry);
                    } else if (sqlTableElement instanceof MySqlPrimaryKey) {
                        MySqlPrimaryKey primaryKey = (MySqlPrimaryKey) sqlTableElement;
                        List<SQLSelectOrderByItem> columns = primaryKey.getColumns();
                        List<String> primaryKeyColumns = Lists.newArrayList();
                        for (SQLSelectOrderByItem column : columns) {
                            primaryKeyColumns.add(column.getExpr().toString());
                        }
                        entry.setPrimaryKeyColumn(primaryKeyColumns);
                    } else {
                        CanalClientCreateTableIndexKey tableIndexKey = new CanalClientCreateTableIndexKey();
                        MySqlKey primaryKey = (MySqlKey) sqlTableElement;
                        List<SQLSelectOrderByItem> columns = primaryKey.getColumns();
                        tableIndexKey.setName(primaryKey.getName().getSimpleName());
                        List<String> keyColumns = Lists.newArrayList();
                        for (SQLSelectOrderByItem column : columns) {
                            keyColumns.add(column.getExpr().toString());
                        }
                        tableIndexKey.setColumnName(keyColumns);
                        indexKeyList.add(tableIndexKey);
                    }
                }
            }
            entry.setIndexKeyList(indexKeyList);
            entry.setColumnEntries(columnEntries);
            createTableEntries.add(entry);
        }
        return createTableEntries;
    }

}
